Android性能优化建议

最近看了一本腾讯测试同学写的书 :《Android移动性能实战》,书中介绍了很多如何检测Android性能问题的工具比如磁盘、内存、CPU、电池等。不过也指出了一些在开发过程中需要注意的点:

磁盘

SharedPreferences

我们知道SharedPreferences底层是使用xml文件来实现的。所以对于SharedPreferences的操作其实是I/O操作,是耗时操作。

commit

每一次commit的调用都会对应一次文件的打开和关闭。commit是同步操作,apply是异步操作。

最佳实践

减少commit的次数; 在一个逻辑操作(方法)中不要多次commitcommit应放在最后。或者使用缓存来保存多次写入的数据,最后提交commit

如果对数据的实时性没有要求,可以使用apply来代替commit

flag的保存与使用

我们通常会使用SharedPreferences来保存一些flag。但是要注意不要随意使用。比如你在一个Recycleview的卡片中,根据一个flag来做不同UI的判断:

class SimpleCard : LinearLayout, AdapterItem{
    fun bindData(...){
        if(Sp.getBoolean(xxx)){
            ...
        }    
    }
}

因为SimpleCardRecycleview中频繁被bindData, 因此SP.getBoolean(xxx)会被频繁调用,前面已经说了SP的读或写是io操作。这种写法势必会造成UI卡顿。

最佳实践

flag一般来说在app启动的时候就已经确定了,所有我们只需要获取一次。可以提前初始化这些flag:

//比如在某个Activity的onCreate

    runOnIoThread({
        FlagCenter.initAllFlag()
    })

//SimpleCard.bindData

 fun bindData(){
        if(FlagCenter.useStyle1){
            ...
        }
    }

ObjectOutputStream

利用它我们可以把对象保存到磁盘中。不过它有一个特点:在保存对象的时候,每个数据成员会带来一次I/O操作。因此如果你对象很多、属性很复杂时,ObjectOutputStreamI/O操作会异常凶猛。

最佳实践

最好在ObjectOutputStream上再封装一个输出流,比如BufferedOutputStream。先把对象写入到这个流中,然后再使用ObjectOutputStream保存到磁盘。

合理设置buffer

在读一个文件我们一般会设置一个buffer。即先把文件读到buffer中,然后再读取buffer的数据。所以: 真正对文件的次数 = 文件大小 / buffer大小 。 所以如果你的buffer比较小的话,那么读取文件的次数会非常多。当然在写文件时buffer是一样道理的。

很多同学会喜欢设置1KB的buffer,比如byte buffer[] = new byte[1024]。如果要读取的文件有20KB, 那么根据这个buffer的大小,这个文件要被读取20次才能读完。

最佳实践 -> buffer应该设置多大呢?

java默认的buffer为8KB,最少应该为4KB。那么如何更智能的确定buffer大小呢?

  1. buffer的大小不能大于文件的大小。
  2. buffer的大小可以根据文件保存所挂载的目录的block size, 什么意思呢? 来看一下SQLiteGlobal.java是如何确定buffer大小的 :
public static int getDefaultPageSize() { 
    return SystemProperties.getInt("debug.sqlite.pagesize", new StatFs("/data").getBlockSize());
}

Bitmap的解码

在Android4.4以上的系统上,对于Bitmap的解码,decodeStream()�的效率要高于decodeFile()decodeResource(), 而且高的不是一点。所以解码Bitmap要使用decodeStream(),同时传给decodeStream()的文件流是BufferedInputStream

最佳实践

val bis =  BufferedInputStream(FileInputStream(filePath))
val bitmap = BitmapFactory.decodeStream(bis,null,ops)

内存

在虚拟机的Heap内存使用超过堆内存最大值就会发生OOM。当手机内存低于内存警戒线时,占用内存越多的APP越有可能被Low Memory Killer给杀掉。

内存泄漏

Activit内存泄漏

Activity对象会间接或者直接引用View、Bitmap等,所以一旦无法释放,会占用大量内存。并且Activity在Destroy的情况下,更新UI是会引发crash的。而Activity内存泄漏最常见的就以下几种case:

  1. 生命周期比Activtiy长的对象持有了Activity的引用。比如在getSystemService时使用了Activity

  2. Activity的内部类作为一个异步的回调监听。比如定义了一个Handler内部类,这时候Handler默认就会引用Activity

最佳实践

在使用getSystemService方法时尽量使用Application, 比如: applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE)

Activity中的Handler定义为静态内部类(解除对Activity的引用),并使用WeakReference<Activity>来引用Activity。

图片

Feed流中的图片如果可以应尽可能降低所占内存大小

对于要加载的图片应做压缩,并在适当的情况下可以在解码时降低质量

最佳实践

对于无透明效果、比较小的图片可以使用RGB_565格式来解码。

在设置解码图片的inSmapleSize参数时应参考要显示的View的宽与高来显示恰当的值。

图片资源不要放在错误的目录

Android的drawable分为好几个层级,比如:drawable、drawable-hdpi、drawable-ldpi、drawable-mdpi等,其实就是对应不同的屏幕密度。Android在获取某个屏幕密度的图片时会去对应的drawable目录下寻找,如果找不到就会取相近目录的资源。但这里是存在一个问题的,举个例子:

比如一张 800 * 480 的图片放置在了ldpi目录。如果480dpi(xxxhdpi)的屏幕要显示这张图片,那么他就会把这张图片放大4倍,即 3200 *1920,然后再去解码。所以图片在内存中被放大了4倍。如果这种case多了,内存很容易爆掉的。

最佳实践

尽量问设计师要高品质图片然后放在高密度目录下,这样在低密度上放大倍数是小于1的,在保证画质的前提下,内存也是可控的。

SparseArray与ArrayMap

SparseArray也是一个map,它的key必须为int类型, 在数据量不大的情况下(千级以内),它的性能会要比HashMap好,并且更升内存。

SparseArray与ArrayMap的key可以为任意类型,当数据量比较小时也可以使用它来代替HashMap

欢迎关注我的Android进阶计划看更多干货

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,480评论 2 59
  • Android性能优化主要从卡顿、内存泄漏和崩溃、代码质量和逻辑、安装包过大四方面入手。在使用时避免出现卡顿,响应...
    fomin阅读 355评论 0 0
  •   作者是武者小路实笃,这本书是我从《文学少女3·沉陷过往的愚者》中了解到的,同时《友情》也是这本书的主题。   ...
    大洪阅读 347评论 0 0
  • 从今天起,做一个不好相处的人! 人如果太好相处, 得到的只会是自己的付出和他人的漠视, 做个不好相处人, 才有人敬...
    上官_b5d2阅读 745评论 0 1